package pl.edu.mimuw.mas.agent.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Vector;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableRowSorter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pl.edu.mimuw.mas.ontology.concept.LogEntry;

/**
 * Panel do wyświetlania logów wizualizowanego systemu.
 */
public class LogPanel extends JPanel {
	private static final long serialVersionUID = -3489040695690458919L;

	private static final int[] COLUMN_WIDTH = { 150, 50, 300 };
	
	private static final int AID_COLUMN = 0;
	// private static final int DATE_COLUMN = 1;
	private static final int VALUE_COLUMN = 2;
	
	private static final String[] COLUMN_TOOL_TIPS = {
			"AID - nazwa agenta z kontenera JADE", 
			"Data zdarzenia", 
			"Treść" };

	private static final String AGENT_ACTIVE_TEXT = "loguj";
	private static final String FILTER_FIELD_DEFAULT_TEXT = "Filtr";
	private static final String SAVE_BUTTON_TEXT = "zapisz logi";
	
	private static final String IO_ERROR_MESSAGE = "Błąd zapisu do pliku";
	private static final String IO_SUCCESS_MESSAGE = "Logi pomyślnie zapisane do pliku";
	
	private JScrollPane scrollPanel;
	private JTable logTable;
	private JPanel bottomPanel;
	private JCheckBox agentActive;
	private JTextField filterField;
	private JButton saveLogTofile;

	private final Object mutex = new Object();
	private TableRowSorter<LogTableModel> logTableSorter;
	
	private final ILogBE myagent;
	
	private final Logger log = LoggerFactory.getLogger(getClass());
	
	public LogPanel(ILogBE myagent) {
		this.myagent = myagent;
		init();
	}
	
	@SuppressWarnings("serial") // Klasy Swinga sa serializowalne
	private void init() {
        logTable = new JTable() {			        	
			protected JTableHeader createDefaultTableHeader() {
                return new JTableHeader(columnModel) {
                    public String getToolTipText(MouseEvent e) {
                        int index = columnModel.getColumnIndexAtX(e.getPoint().x);
                        int realIndex = columnModel.getColumn(index).getModelIndex();
                        return COLUMN_TOOL_TIPS[realIndex];
                    }
                };
            }
        };
        logTable.setModel(new LogTableModel());
        
        // Proporcje wierszy
		for (int i = 0; i < COLUMN_WIDTH.length; i++) {
		    logTable.getColumnModel().getColumn(i).setPreferredWidth(COLUMN_WIDTH[i]);
		    if (COLUMN_WIDTH[i] == 5) {
		    	logTable.getColumnModel().getColumn(i).setMaxWidth(COLUMN_WIDTH[i]);	    	
		    }
		}
		logTable.setFillsViewportHeight(true);
		
		// Sortowanie wierszy - wyłączone w LogTableSorter.isSortable
		logTableSorter = new LogTableSorter((LogTableModel) logTable.getModel());
		logTable.setRowSorter(logTableSorter);		
		
		// Checkbox - czy logować
		agentActive = new JCheckBox(AGENT_ACTIVE_TEXT);
		agentActive.setSelected(myagent.isActive());
		agentActive.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				myagent.toogleActivity();
			}
        });

		// Zapisywanie do pliku
		saveLogTofile = new JButton(SAVE_BUTTON_TEXT);
		saveLogTofile.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				final JFileChooser fc = new JFileChooser();

				if (fc.showSaveDialog(saveLogTofile) == JFileChooser.APPROVE_OPTION) {
					File file = fc.getSelectedFile();
					log.info("Zapisuje logi do pliku: {}", file.getPath());

					synchronized (mutex) {
						try {
							BufferedWriter out = new BufferedWriter(new FileWriter(file));
							
							for (int row = 0; row < logTable.getRowCount(); row++) {
								int realRow = logTable.convertRowIndexToModel(row);

								for (int col = 0; col < logTable.getColumnCount(); col++) {
									int realCol = logTable.convertColumnIndexToModel(col);									
									out.write((String) logTable.getModel().getValueAt(realRow, realCol));
									out.write("\t");
								}
								out.write("\n");
							}

							out.close();
							
							log.info("Zapis logów do pliku zakonczony pomyślnie");
							JOptionPane.showMessageDialog(saveLogTofile, IO_SUCCESS_MESSAGE, IO_SUCCESS_MESSAGE, JOptionPane.INFORMATION_MESSAGE);
						} catch (IOException e) {
							JOptionPane.showMessageDialog(saveLogTofile, IO_ERROR_MESSAGE, IO_ERROR_MESSAGE, JOptionPane.ERROR_MESSAGE);
							log.error("Błąd zapisu logów do pliku : {}", file.getPath(), e);
						}
					}
				}
			}
		});
		
        // Filtrowanie
		filterField = new JTextField();
		filterField.setMaximumSize(new Dimension(300, 20));
		filterField.setPreferredSize(new Dimension(300, 20));
		filterField.setText(FILTER_FIELD_DEFAULT_TEXT);
		filterField.setForeground(Color.GRAY);

		filterField.addFocusListener(new FocusListener() {
			public void focusGained(FocusEvent e) {
				if (filterField.getText().equals(FILTER_FIELD_DEFAULT_TEXT)) {
					filterField.setText("");
				}
			}

			public void focusLost(FocusEvent e) {
				// if (filterField.getText().trim().isEmpty()) {
				// 	filterField.setText(FILTER_FIELD_DEFAULT_TEXT);
				// }
			}
		});
		
		filterField.getDocument().addDocumentListener(new DocumentListener() {
			public void changedUpdate(DocumentEvent e) {
				createFilter();
			}

			public void insertUpdate(DocumentEvent e) {
				createFilter();
			}

			public void removeUpdate(DocumentEvent e) {
				createFilter();
			}
		});

		// Dolny panel
		bottomPanel = new JPanel();
		bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS));
		bottomPanel.add(agentActive);	
		bottomPanel.add(Box.createRigidArea(new Dimension(5,0)));
		bottomPanel.add(saveLogTofile);
		bottomPanel.add(Box.createHorizontalGlue());
		bottomPanel.add(filterField);
		
        // Widok manager agentow
		scrollPanel = new JScrollPane(logTable);		
        
        GroupLayout agentsPanelLayout = new GroupLayout(this);
        this.setLayout(agentsPanelLayout);
        agentsPanelLayout.setHorizontalGroup(
            agentsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(agentsPanelLayout.createSequentialGroup()
                .addGroup(agentsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
                	.addComponent(bottomPanel)
                	.addComponent(scrollPanel)))
        );
        
        agentsPanelLayout.setVerticalGroup(
            agentsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(GroupLayout.Alignment.CENTER, agentsPanelLayout.createSequentialGroup()
                .addComponent(scrollPanel)
                .addComponent(bottomPanel))
        );	
	}
	
	/**
	 * Załóż filtr na wyświetlane wiesze w tabeli. Filtruje względem kolumn "AID" lub "Treść".
	 */
	private void createFilter() {
		try {
			RowFilter<LogTableModel, Object> rf;
			rf = RowFilter.regexFilter(filterField.getText(), AID_COLUMN, VALUE_COLUMN);
			logTableSorter.setRowFilter(rf);
		} catch (java.util.regex.PatternSyntaxException e) {
		}
	}
	
	/**
	 * Wyświetl zalogowany element w tabelce.
	 */
	public void addLogEntry(LogEntry entry) {
		((LogTableModel) logTable.getModel()).addRow(entry);
        logTable.scrollRectToVisible(logTable.getCellRect(logTable.getRowCount() - 1, 0, true));
	}
	
	/**
	 * Model danych wyświetlanych w tabeli, domyślny model rozszerzony o
	 * definicje kolumn oraz dodatkowe metody importu danych.
	 * 
	 *
	 */
	private class LogTableModel extends DefaultTableModel {
		private static final long serialVersionUID = 3412157070932550714L;
		
		private final SimpleDateFormat formatter;

		public LogTableModel() {
			super(new Object[][] {}, new String[] { "AID", "Data", "" });
			this.formatter = new SimpleDateFormat("HH:mm:ss");
		}

		public void addRow(LogEntry entry) {			
			Vector<Object> vec = new Vector<Object>(3);
			vec.add(entry.getAgentId());
			vec.add(formatter.format(entry.getDate()));
			vec.add(entry.getValue());
			
			if(log.isTraceEnabled()) {
				log.trace(entry.toString());
			}
			
			synchronized (mutex) {
				addRow(vec);				
			}
		}
		
		private Class<?>[] types = new Class[] { String.class, String.class, String.class }; 
		
		public Class<?> getColumnClass(int columnIndex) {
			return types[columnIndex];
		}

		public boolean isCellEditable(int rowIndex, int columnIndex) {
			return false;
		}
	}
	
	private class LogTableSorter extends TableRowSorter<LogTableModel> {
		public LogTableSorter(LogTableModel model) {
			super(model);
		}
		
		@Override
		public boolean isSortable(int column) {
			return false;
		}
	}
}
